iT邦幫忙

2025 iThome 鐵人賽

DAY 24
0
Software Development

30天從基礎學起Java,直到做出我的第一個遊戲系列 第 24

Day 24:Java Snake Game 主畫面UI升級(一)

  • 分享至 

  • xImage
  •  

今天我想要更新遊戲主畫面的UI,讓玩家在進遊戲時會有start、setting、help這樣的選單畫面
因此,我打算透過多個Panel切換的方式進行主畫面、遊戲本體與設定畫面等等的畫面切換

首先我先開一個MainPanel,作為點開遊戲時呈現出來的主畫面

import javax.swing.JPanel;
 import javax.swing.JLabel;
 import javax.swing.JButton;
 import java.awt.Dimension;
 import java.awt.Color;
 import java.awt.Font;

 public class MenuPanel extends JPanel{
     // 使用和遊戲畫面同樣的寬跟高
     static final int PANEL_WIDTH = 800;
     static final int PANEL_HEIGHT = 500;

     // 展示文字
     JLabel titleLabel;

     // 建立三個功能按鈕
     JButton startButton;
     JButton settingButton;
     JButton helpButton;

     Font titleFont; // 標題的文字形式
     Font selectFont; // 選項的文字形式

     MenuPanel(){
         titleFont = new Font("MV Boli", Font.BOLD, 30);
         selectFont = new Font("Times New Roman", Font.PLAIN, 20);

         this.setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT));
         this.setBackground(Color.darkGray);
         this.setLayout(null);

         titleLabel = new JLabel("Snake Game");
         titleLabel.setFont(titleFont);
         titleLabel.setForeground(Color.WHITE);
         titleLabel.setBounds(100, 50, 600, 100);

         startButton = new JButton("Start Game");
         startButton.setFont(selectFont);
         startButton.setBounds(300, 250, 200, 50);

         settingButton = new JButton("Settings");
         settingButton.setFont(selectFont);
         settingButton.setBounds(300, 320, 200, 50);

         helpButton = new JButton("Help");
         helpButton.setFont(selectFont);
         helpButton.setBounds(300, 390, 200, 50);

         this.add(titleLabel);
         this.add(startButton);
         this.add(settingButton);
         this.add(helpButton);
     }
 }

建立完成後,我本來的想法是使用點擊按鈕後開啟新視窗的方式,可是我又覺得這樣會降低使用者體驗(每按一個按鈕就會跳出一個新頁面),因此我向Gemini求助,他建議我可以使用CardLayout這個系統
image

於是,我開始學習如何使用CardLayout
我需要在我的程式入口點,也就是最初創建的GameFrame,將其設為一個「容器」,用這個frame作為呼叫各個panel的總管

要做到這點,我們需要在GameFrame中宣告一個虛擬的panel,而這個panel的用意就是呼叫我們需要的panel,可以其稱為「牌堆」,用來存放各種不同的Panel(也就是各個卡牌)

import javax.swing.JFrame;
import java.awt.CardLayout;
import javax.swing.JPanel;

public class GameFrame extends JFrame{
    CardLayout cardLayout; // 宣告一個CardLayout物件,我們之後還會用到它
    
    JPanel mainPanel; // 宣告一個mainPanel作為我們的牌堆,用來存放各個panel
    // 然後對於現有的兩個Panel,各宣告出他們的物件
    MenuPanel menuPanel;
    GamePanel gamePanel;

    GameFrame(){
        cardLayout = new CardLayout();
        mainPanel = new JPanel(cardLayout); // 給mainPanel傳入cardLayout參數

        // 我們需要修改Panel的constructor參數,才能傳當前的GameFrame參數(連結到牌堆)
        menuPanel = new MenuPanel(this);
        gamePanel = new GamePanel(this);

        // 將卡牌加入牌堆並給他們命名(JPanel panel, String name),之後要用對應名稱呼叫他們
        mainPanel.add(menuPanel, "Menu");
        mainPanel.add(gamePanel, "Game");

        this.add(mainPanel);
        this.setTitle("SnakeGame");
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setResizable(false);
        this.pack();
        this.setVisible(true);
        this.setLocationRelativeTo(null);
    }
    
    // 切換面板的method
    public void showPanel(String panelName){
        // 使用show,檢查在mainPanel(牌堆)中是否有正在搜尋的panel(卡牌),如果有就顯示在畫面上
        cardLayout.show(mainPanel, panelName);
        
        // 我們把GamePanel中的startGame()從constructor移除並設為public
        // 當檢查到我們正在呼叫mainPanel中的"Game"時,才觸發GamePanel中的開始遊戲
        if ("Game".equals(panelName)) {
            gamePanel.startGame();
        }
    }
}

接下來我們修改GamePanel和MenuPanel

// 已知我們將每個panel的constructor都改為傳入一個GameFrame參數

public class GamePanel extends JPanel{
    GameFrame gameFrame; // 新增一個gameFrame變數
    // ...
    
    GamePanel(GameFrame frame){
        this.gameFrame = frame; // 接收傳入的參數
        // ...
    }
    
    // 現在知道遊戲開始是由外部觸發的(當menuPanel呼叫"Game"),因此我們只需要修改gameOver
    private void gameOver(){
        // 遊戲結束時先初始化變數資料
        gameFrame.showPanel("Menu"); 
        // 使用gameFrame,呼叫showPanel method,告訴他我們要回到主畫面
    }
}
public class MenuPanel extends JPanel{
    GameFrame gameFrame;
    // ...
    
    MenuPanel(GameFrame frame){
        this.gameFrame = frame;
        // ...
        startButton.addActionListener(new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent e){
                gameFrame.showPanel("Game");
            }
        }); // 新增startButton的觸發事件,用Anonymous Inner Class傳入事件效果,呼叫Game卡牌
    }
}

image

到這邊就可以發現,我們的邏輯基本上就是:
一開始到主畫面,可以點擊開始遊戲、設定與幫助
我們會宣告出好幾個不同的Panel,分別對應到各個不同的功能,而這些Panel則是幫忙分擔GamePanel的任務
這樣子GamePanel只需要專心完成他的"Game"(遊戲)任務,剩下的交給不同的組件即可
而這個切換的過程則是透過CardLayout進行無縫切換
因此我今天學會如何透過這個手段增加使用者體驗之後,明天我要做的就是完善其他不同的Panel,包含SettingPanel、HelpPanel、GameOverPanel等,畢竟現在只有完成主畫面與遊戲本體的切換,還沒有設定幫助與結束頁面!


上一篇
Day 23:Java Snake Game 增加遊戲功能(二)
下一篇
Day 25:Java Snake Game 主畫面UI升級(二)
系列文
30天從基礎學起Java,直到做出我的第一個遊戲25
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言